Technical Note TN2005
SimpleCocoaApp: An Overview


目次

このテクニカルノートでは、SimpleCocoaApp コードサンプルを詳細に検討して、これを Cocoa プログラミングのヒントと原則を論じるための糸口として利用します。SimpleCocoaApp サンプルをこのテクニカルノートからダウンロードし、このテクニカルノートの内容を参考にしながら、実際のソースコードを直接調べてみることをお勧めします。SimpleCocoaApp が実行する処理すべてがあらゆるアプリケーションで使用されるわけではありませんが、それらすべてはさまざまなアプリケーションに応用できます。

このテクニカルノートは Cocoa プログラミングにそれほど習熟していない読者向けに書かれていますが、読者が少なくとも Objective-C

Project Builder、および Interface Builder の基礎知識をある程度身に付けていることを前提にしています。このテクニカルノートの末尾には、実際に開発を行うときに参考になる Cocoa 関連マニュアルへのリンクが一覧表示されています。このテクニカルノートの目的は、強力なアプリケーション開発システムと API の一端を具体的に示すことで読者の視野を開くことにあります。これまで曖昧だったいくつかの概念がより明瞭なものになり、読者が Cocoa の世界を探求するための一歩を踏み出すことができるとすれば、筆者として望外の幸せです。

更新日: 2000 年 10 月 25 日






SimpleCocoaApp とは

SimpleCocoaApp は非常にシンプルなアプリケーションですが、正真正銘のアプリケーションです。SimpleCocoaApp は 20 行にも満たない短いコードから構成されていますが、その中にはさまざまな機能が満載されています。このアプリケーションはたしかにそれほど役に立つ動作を行うわけではありませんが、ラジオボタン、ポップアップメニュー、「ヘルプ」メニュー、ウインドウ処理、ボタンクリック、テキストフィールド入力、および「About」ボックスを処理します。次に、実行中のこのアプリケーションの画面を示します (図 1)。



図 1 SimpleCocoaApp のメインウインドウ



SimpleCocoaApp では、3 つの異なるカスタムクラスからインスタンス化 (作成) された 3 つのメインオブジェクトを使用します。

  • Hello1: -message1:、-message2:、および -message3: メソッドの実装を含みます。
  • Hello2: -message1:、-message2:、および -message3: メソッドの異なる実装を含みます。
  • HelloController: GUI と Hello オブジェクトとの間のデータのやり取りを処理すると同時に、「Help」メニューから「SimpleCocoaApp Help」が選択されたときに、それに応答して ReadMe ヘルプファイルを表示します。通常、ビュー (GUI) とモデル (実際のデータオブジェクト、ここでは Hello オブジェクト) との間でデータのやり取りを管理するには、このような「コントローラ」オブジェクトを使用することをお勧めします。この方法は、モデル - ビュー - コントローラデザインパラダイムと呼ばれます。

次に、それぞれのオブジェクトが相互にどのように動作するかを示します。

Cocoa 環境で動作する大部分のコントロールは、それに関連付けられた「ターゲット」オブジェクトを持ち、そのコントロールがアクティブになるとき、ターゲットオブジェクトには「アクションメッセージ」(メソッド呼び出しに似たメッセージ) が送信されます。画面上でのコントロールの体裁と装置としてのコントロールの機能は Cocoa のインフラストラクチャによって処理されるため、実装する必要があるのは、ターゲットオブジェクトとそれに対応するアクションメッセージのみです。したがって、ここでは次のような相互動作が実行されます。

  • ラジオボタンの 1 つを選択するとき、アクションメッセージがそのラジオボタンのターゲット (HelloController) に自動的に送信され、ターゲットは、「Say Hello...」ボタンに対するアクションメッセージを -message1:、-message2:、または -message3: に変更します。
  • ポップアップメニューのメニュー項目を選択するとき、アクションメッセージがポップアップメニューのターゲット (やはり HelloController) に送信され、ターゲットは「Say Hello...」ボタンのターゲットを異なる「Hello」オブジェクト (Hello1 または Hello2) に変更します。
  • 「Say Hello...」をクリックするとき、アクションメッセージ (ラジオボタンで選択した) がそのターゲット (ポップアップメニューで選択した) に送信され、ターゲットは単純にアラート (「message #3」を選択している場合は「My Message...」テキストフィールドに入力されているテキスト) を表示します 。

以下のセクションでは、このアプリケーションを書くプロセスを詳細に検討してみましょう。

ページの先頭に戻る



Interface Builder で行うセットアップ作業

通常、Project Builder で新しい Cocoa アプリケーションを作成した後で最初に行う必要があるのは、MainMenu.nib を開いて、Interface Builder でユーザインタフェースとクラスをデザインすることです。SimpleCocoaApp のコードはすでに完成しているため、単に Project Builder で .pbproj ファイルをダブルクリックしてプロジェクトを開き、左側のファイルリストに表示されている MainMenu.nib をダブルクリックするだけです。MainMenu.nib を開くと、nib ウインドウの「Classes」タブには Hello1、Hello2、および HelloController クラスが表示され、「Instances」タブには Hello1、Hello2、HelloController、および MyWindow のインスタンスが表示されます。SimpleCocoaApp を作成するときに最初に行ったのは、Hello1、Hello2、および HelloController カスタムクラスを作成することでした (「Classes」タブで NSObject のようなクラスが選択されている状態で return キーを押して)。さらに、これらのカスタムクラスのインスタンスを作成した上で、コントロール/ビューをウインドウ (ここでは MyWindow) の所定の位置にドラッグしました。Apple では、ユーザの一貫した使用感を維持するため、開発者が Aqua ユーザインタフェースのガイドラインに準拠することを推奨しています。なお、Aqua ユーザインタフェースの詳細については、このテクニカルノートの末尾に挙げてある関連マニュアルのリンクを参照してください。実際のアプリケーションを開発するときにこの段階に達した場合は、UI とカスタムクラスを接続して、それらが相互にメッセージをやり取りしたり、何かの処理 (値の利用、値の設定、動作の変更など) を実行するように相互に指示できるようにする必要があります。ここでは、Interface Builder のみを使用して、この作業をグラフィカルに実行しますが、実際のアプリケーション開発でもコードにまったく手を加える必要がないことがしばしばあります。Interface Builder を使用すると、インスタンス化されているオブジェクトを次の 2 つの方法のいずれかで接続することができます。

  1. どのオブジェクト (特にコントロール) も「アウトレット」を持つことができます。アウトレットとは、他のオブジェクトをポイントするインスタンス変数のことです。プログラムのコントローラオブジェクトにさまざまなユーザインタフェース要素へのアウトレットをセットアップしておくと、それらの要素にステータスの変更などを指示できるため、しばしば便利なことがあります。
  2. コントロールには、他のオブジェクトとの接続に使用できる特殊な方法が用意されています。つまり、コントロールは、任意のオブジェクトをそのコントロールのターゲットとして割り当てることができ、さらに、オブジェクト内の特定のメソッドをそのコントロールがアクティブになるときに呼び出されるアクションとして指定することができます。

これらの接続のいずれかを確立するには、まずメッセージの送信元となるオブジェクトを control キーを押しながらクリックし、カーソルをメッセージの送信先となるオブジェクトまでドラッグします。これにより、2 つのオブジェクトがグレイの線で結ばれます。この後は、inspector ウインドウでアウトレットとアクションをナビゲートし、「Connect」をクリックして接続を確立することができます。アウトレット接続を確立するためにある方向にドラッグした場合、ターゲット-アクション接続を確立するためにはオブジェクト間を逆方向にドラッグする必要があります。要するに、メッセージが送信される方向に control ドラッグを行うということです。

たとえば、図 2 は、インスタンス化された Hello1 オブジェクト内で、Hello1 の -message3: メソッドに対して表示するテキストを保持することになる NSTextField オブジェクトへの「messageTextField」という名前のアウトレットを接続しているところを示しています。この接続を確立するためには、Hello1 オブジェクトから NSTextField に control ドラッグを行います。これは、プログラムが実行されたときに、Hello1 オブジェクトがこのテキストフィールドにメッセージを送信するためです。



図 2 Hello1 と NSTextField との間のアウトレット接続を確立しているところ



一方、図 3 は、「Say Hello...」ボタンのターゲット-アクション接続をセットアップしているところを示しています。このボタンをクリックすると、アクションメッセージがそのターゲットに送信されるため、メッセージがボタンからターゲットに流れるように、ボタンから Hello1 に向かって control ドラッグを行います。そして、inspector ウインドウで「message1:」というアクションを選択して、「Connect」をクリックします。



図 3 「Say Hello...」ボタンと Hello1 との間のターゲット-アクション接続を確立しているところ



 

ユーザインタフェースをグラフィカルにセットアップする作業にはそれなりの時間がかかりますが、長い目で見れば、純粋なソースコードで同じ作業を行うことを考えれば、はるかに時間の節約になります。この時点でコマンドキーを押しながら R キーを押すと (「File」メニューの「Test Interface」)、コードをまったく必要とすることなく、アプリケーションのユーザインタフェース部分の大部分を実行することができます。つまり、コントロールを動作させ、ボタンをクリックしたり (ただし、カスタムコードオブジェクトに接続されたアクションが呼び出されることはありません)、メニューを表示することなどが可能になります。これらすべては、Cocoa でコードを 1 行も書くことなく、何もせずに手に入れることのできる機能です。

ソースコードに戻る前に実行する必要のある最後のステップは、カスタムクラスに対応したスケルトンヘッダ/ソースファイルを作成することです。このためには、「Classes」タブでカスタムクラスを control クリックして、「Create Files...」を選択します。以上の操作で、ユーザインタフェースのデザイン、カスタムオブジェクトのインスタンス化、およびすべての接続の確立を終えて、独自の機能を実装する SimpleCocoaApp のソースコードに戻ることができます。

ページの先頭に戻る



Main.m

                  
int main(int argc, const char *argv[]) {
    return NSApplicationMain(argc, argv);
}

リスト 1 非常に簡潔な main.m



リスト 1 に示すように、main() について多くを語る必要はありません。大部分の読者は、ここに書かれている通りのものとして main() を理解できるはずです。main() は NSApplication をジャンプスタートさせるだけで、NSApplication がアプリケーションの残りの部分、イベントループ、およびその他すべてを実行します。ここで指摘する価値があるとすれば、SimpleCocoaApp のすべてのコードは Objective-C を使って書かれていますが、Java を使って Cocoa アプリケーションを書くこともでき、しかも大部分のオブジェクト/メソッド名が同じであるという点のみです。

ページの先頭に戻る



Hello1 および Hello2 オブジェクト

Hello1 および Hello2 クラスは事実上同じものであり、唯一の違いは、これらのクラスが表示するダイアログに配置されたタイトル文字列が異なる点のみです。ここで Hello1 について説明した内容は Hello2 にも当てはまります。このようにほとんど違いのない 2 つのクラスを用意したのは、コントロールのターゲットをその場で切り替える方法を具体的に示すためです。したがって、「Say Hello...」ボタンをクリックしたときにコードのどの行が実行されるかを確認できるのは実行時のみということになります。

ここでは、Hello1 クラスの実装ファイルである Hello1.m を検討してみましょう。空のメソッドを含むクラススケルトンは Interface Builder によって簡単に生成できますが、メソッドの実装を書く作業はプログラマに委ねられています。Hello1 は、-message1:、-message2:、および -message3: という、実装すべき 3 つのメソッドを持っています。



- (IBAction)message1:(id)sender{
    //標準アラートを使ってメッセージを表示する
    NSRunAlertPanel(@"Message1, Hello1",@"Hello, Cocoa!",
        @"OK",NULL,NULL);
}

リスト 2 -message1: と -message2: は両方とも同じように実装される



NSRunAlertPanel() (リスト 2 を参照) はユーザにアラートボックスを表示しますが、パラメータを使って、表示されるアラートボックスの内容を構成することができます。最初のパラメータはアラートタイトルで、2 番目のパラメータはアラートメッセージです。また、それに続く 3 つのパラメータはアラート内に 3 つのボタンを表示します。ボタンのパラメータに NULL を渡すと、対応するボタンがアラートから削除されます。ここでは、NSRunAlertPanel() から返される戻り値については特に詳しく言及しませんが、それがクリックされたボタンを表す整数 (対応する定数があらかじめ定義されています) を返すことに注意する必要があります。



- (IBAction)message3:(id)sender
{
    //標準アラートを使って、messageTextField がポイントする
    //NSTextField に入力されているテキストを表示する
    NSRunAlertPanel(@"Message3, Hello1",
        [messageTextField stringValue],@"OK",NULL,NULL);
}

リスト 3 -message3: は他の 2 つのメッセージルーチンよりも若干賢い



リスト 3 に示すように、-message3: は NSRunAlertPanel() にハードコードされたテキスト行ではなく、messageTextField インスタンス変数がポイントする NSTextField のコンテンツを表示させます。前のセクションでは、SimpleCocoaApp のデザインの一部として、Interface Builder で messageTextField アウトレットを NSTextField コントロールに接続しているため、ここで行っているように、NSTextField の値を取り出すことができます。-stringValue メソッドは、NSTextField のコンテンツを NSString として返します。

ページの先頭に戻る



HelloController オブジェクト

HelloController は、SimpleCocoaApp の背後で実際に「頭脳」として動作するクラスです。HelloController はアプリケーションが起動したときに必要となるすべての処理を実行し、アクションメッセージとターゲットの切り替えを処理して、ヘルプが適切に表示されていることを確認します。これらすべての処理は 3 つの簡潔なメソッドを使用して実行されます。




- (IBAction)switchMessage:(id)sender
{
    // sender はラジオボタンを含む NSMatrix。
    // sender に選択されている行 (ラジオボタン) を要求し、
    // 戻り値に 1 を加算してカウントが 1 から始まるように調整する。
                  
    int which=[sender selectedRow]+1;
                  
    // 次に、NSButton のアクションがラジオボタンの選択に
    // 対応したメッセージとなるように設定する。
    // +[NSString stringWithFormat:...] は、"message" と
    // メッセージ番号を連結するために使用される。
    // NSSelectorFromString は、メッセージ名文字列を
    // Objective-C が使用できる実際のメッセージ構造体に変換する。
                  
    [helloButton setAction:NSSelectorFromString([NSString
        stringWithFormat:@"%@%d:",@"message",which])];        
}

リスト 5 - switchMessage: は、ユーザが異なるラジオボタンをクリックして、表示する異なるメッセージを選択したときに呼び出される



リスト 5 で実際に重要なのは最後の行です。+stringWithFormat: は、printf 形式のフォーマット文字列とパラメータリストを受け付け、C の sprintf ルーチンの考え方に似た方法で文字列を生成します。ここでは、その戻り値を NSSelectorFromString() 関数に渡します。この関数は、名前が暗示しているように、渡された文字列を「セレクタ」、つまりメソッドの完全な名前/識別子 ("method:withParameter1:parameter2:parameter3:" の形式の) に変換します。この後、セレクタは helloButton の -setAction メソッドに渡され、クリックされたときに helloButton によって呼び出されるアクションを変更します。




- (IBAction)switchObject:(id)sender
{
    // sender は Hello オブジェクトの選択肢を含む NSPopUpMenu。
    // sender に選択されているメニュー項目を要求し、
    // 戻り値に 1 を加算してカウントが 1 から始まるように調整する。
                  
    int which=[sender indexOfSelectedItem]+1;
                  
    // 選択されているメニュー項目に基づいて、
    // helloButton のターゲット (受信オブジェクト)
    // が hello1 または hello2 をポイントするように設定する。
                  
    if (which==1)
    [helloButton setTarget:hello1];
    else
    [helloButton setTarget:hello2];
}

リスト 6 -switchObject: は、ユーザがポップアップメニューを使用してメッセージを受信する異なるオブジェクトを選択したときに呼び出される



helloButton は、ウインドウ上の「Say Hello...」NSButton をポイントするアウトレット (Interface Builder で接続した) です。ここでは、setTarget メソッドを呼び出し、ポップアップメニューで選択されたオブジェクトに従って、ボタンのターゲットを hello1 または hello2 のいずれかに変更します。その結果、将来このボタンがクリックされるときには、新しいターゲットにアクションメッセージが送信されることになります。




- (IBAction)showHelp:(id)sender
{
    // -[NSWorkspace openFile:withApplication:] を呼び出して、
    // ヘルプビューアで目的の html ファイルを開く。
    // -[NSBundle pathForResource:ofType:] を使って、
    // 目的の html ファイルの検出を支援する。
                  
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ReadMe"
        ofType:@"html"];
    if (path) {
        [[NSWorkspace sharedWorkspace] openFile:path withApplication:
        @"Help Viewer"];
    }
}

リスト 7 -showHelp: は、ユーザが「ヘルプ」メニューから「SimpleCocoaApp Help」を選択したときに呼び出される



リスト 7 のルーチン全体は、Interface Builder で「Help」メニュー項目に対するアクションメソッドとして接続されています。+[NSBundle mainBundle] はアプリケーション独自のバンドルをポイントする NSBundle を返すため、そのバンドルからリソース、ヘルプファイルなどをロードすることができます。ここでは、+[NSBundle mainBundle] を使って、ReadMe ファイルへのパスを検出します。+[NSWorkspace sharedWorkspace] (共有ワークスペースとは、Finder を Cocoa 流に表現したもの) は、このパスとアプリケーション名を組み合わせて使用し、指定されたアプリケーションでそのファイルを開きます。

ページの先頭に戻る



要約

すべて引っくるめても約 15 行のソースコードだけで、ここまで見てきたすべての機能を手に入れることができます。このテクニカルノートを読んだ開発者の今後の課題は、Cocoa のフレームワークをできるかぎり多く学ぶことです。そうすれば、開発者は、すでに Cocoa によって実装されている処理をあらためて自分自身で再実装する必要がなくなります。次に、参考になるマニュアルと、このテクニカルノートで取り上げたサンプルコードへのリンクを示します。

 



オンラインマニュアル

Mac OS X Developer Documentation (as well as Adopting Aqua documentation)

Cocoa Developer Documentation

Developer Tools

Objective-C Manual

Project Builder

Back to top



ファイルのダウンロード

SimpleCocoaApp サンプルコードの圧縮ファイル (45K)

Download

ページの先頭に戻る